<template>
    <div class="content" id="appContent">
        <div class="drawer" @click="showDrawer">
            <DArrowRight />
        </div>
        <div class="chart">
            <div
                class="graphDiv"
                id="graphDiv"
                style="width: 100%; height: 90%"
            ></div>
            <div class="tip" v-if="dataStatus === 'select'">
                <Choose />
                <div class="tip--text">请打开侧边栏选择数据</div>
            </div>
            <div class="tip" v-if="dataStatus === 'loading'">
                <Loading />
                <div class="tip--text">数据加载中...</div>
            </div>
            <div class="tip" v-if="dataStatus === 'error'">
                <NotFound />
                <div class="tip--text">
                    该设备当前日期没有数据，请重新选择！
                </div>
            </div>
        </div>
        <el-drawer
            class="drawerContent"
            style="color: #ffffff; background-color: #21262f"
            v-model="drawer"
            title="曲线设置"
            :with-header="false"
            :modal="false"
            direction="ltr"
            size="100%"
            modal-class="drawerMask"
        >
            <div class="iconRight" @click="showDrawer">
                <DArrowLeft />
            </div>
            <el-config-provider :locale="zhCn">
                <div class="drawerContent--setting">
                    <div class="subject">
                        <div v-if="subjectStyle === false">
                            点击开关转换主题：白色模式
                        </div>
                        <div v-if="subjectStyle === true">
                            点击开关转换主题：黑色模式
                        </div>
                        <el-switch
                            @change="styleChange"
                            v-model="subjectStyle"
                        />
                    </div>
                    <div class="selectDate">
                        <div>请选择时间</div>
                        <div>
                            <el-date-picker
                                v-model="dateData"
                                type="date"
                                placeholder="选择时间"
                                format="YYYY/MM/DD"
                                value-format="YYYY-MM-DD"
                                :default-value="new Date()"
                                popper-class="datePicker"
                            />
                        </div>
                    </div>
                    <div class="deviceId">
                        <div>选择设备</div>
                        <div>
                            <el-select
                                v-model="deviceId"
                                placeholder="设备"
                                @change="getSetting"
                                style="width: 240px"
                            >
                                <el-option
                                    v-for="item in deviceList"
                                    :key="item.value"
                                    :label="item.name"
                                    :value="item.id"
                                />
                            </el-select>
                        </div>
                    </div>
                    <div class="selectSetting">
                        <div>选择已有设置</div>
                        <div>
                            <el-select
                                v-model="settingTitle"
                                placeholder="选择设置"
                                style="width: 240px"
                                @change="idGetSettings"
                            >
                                <el-option
                                    v-for="item in settingOptions"
                                    :key="item.id"
                                    :label="item.title"
                                    :value="item.id"
                                />
                            </el-select>
                        </div>
                    </div>
                    <div class="dataTable">
                        <el-table
                            :data="curveShow"
                            style="width: 100%; background-color: #21262f"
                            max-height="800"
                            :row-style="getRowStyle"
                        >
                            <el-table-column
                                prop="name"
                                label="曲线名称"
                                width="150"
                            />

                            <!-- 偏移乘积（输入框） -->
                            <el-table-column label="偏移乘积" width="100">
                                <template #default="{ row }">
                                    <el-input
                                        v-model="row.offsetMultiplication"
                                        size="small"
                                    />
                                </template>
                            </el-table-column>

                            <!-- 偏移增加（输入框） -->
                            <el-table-column label="偏移增加" width="100">
                                <template #default="{ row }">
                                    <el-input
                                        v-model="row.offsetIncrement"
                                        size="small"
                                    />
                                </template>
                            </el-table-column>

                            <!-- 是否展示（开关） -->
                            <el-table-column label="是否展示" width="100">
                                <template #default="{ row }">
                                    <el-switch
                                        @change="curveVisible(row.index)"
                                        v-model="row.visible"
                                    />
                                </template>
                            </el-table-column>
                        </el-table>
                    </div>
                    <div class="clickButton">
                        <el-button
                            class="button"
                            @click="getDeviceData"
                            type="success"
                            >绘制曲线</el-button
                        >
                        <el-button
                            class="button"
                            @click="startOffset"
                            type="success"
                            >确定偏移</el-button
                        >
                        <el-button
                            class="button"
                            type="success"
                            plain
                            @click="uploadSetting"
                            >添加设置</el-button
                        >
                        <el-button
                            class="button"
                            type="success"
                            plain
                            @click="clearSetting"
                            >清除设置</el-button
                        >
                    </div>
                </div>
            </el-config-provider>
        </el-drawer>

        <div class="labelDiv">
            <div id="content" class="label"></div>
        </div>
    </div>
</template>

<script setup>
import Dygraph from 'dygraphs'
import { onMounted, onUnmounted, ref } from 'vue'
import colors from '../utils/colors.js' // 色卡
import { DArrowLeft, DArrowRight } from '@element-plus/icons-vue'
import {
    getDeviceList,
    getDeviceCsv,
    getDeviceSettings,
    addDeviceSettings,
    getSettingsInfoUseId,
} from '../utils/fetch.js'
import { ElConfigProvider, ElMessage, ElMessageBox } from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import Loading from '../components/Loading.vue'
import Choose from '../components/Choose.vue'
import NotFound from '../components/NotFound.vue'

// 用于控制页面主题存储
const subjectStyle = ref()

// 控制抽屉
const drawer = ref(false)

// 构建一个Dygraphs容器
let graph = null

// 图表题目
const title = ref('')

// 错位计数器
let count = 0

// 存储曲线展示的数据容器
let visibility = []

// 日期存放
const dateData = ref(new Date().toLocaleDateString())

// 曲线数据来源的CSV文件路径
const csvData = ref() // 确保文件在 public 目录下

// 用于下拉框选中设置时显示设置名称
const settingTitle = ref()

// 用于在下拉框中存储设置列表
const settingOptions = ref([])

// 存放展示的曲线索引
const curveShow = ref([])

// 获取csv表的表头数据，用来控制曲线的展示与否
const csvHeaders = ref([])

// 用于下拉框选中设备时存储设备id
const deviceId = ref()

// 在下拉框中展示设备列表
const deviceList = ref([])

// 设置起名
const settingName = ref()

// 数据状态
const dataStatus = ref('select')

const uploadSetting = () => {
    ElMessageBox.prompt('请为你的设置起个名吧', '提示', {
        confirmButtonText: '确认上传',
        cancelButtonText: '取消上传',
    })
        .then(({ value }) => {
            settingName.value = value
            addSettings()
            ElMessage({
                type: 'success',
                message: '上传设置成功',
            })
        })
        .catch(() => {
            ElMessage({
                type: 'info',
                message: '取消上传',
            })
        })
}

const clearSetting = () => {
    curveShow.value.forEach((item, index) => {
        item.offsetMultiplication = 1
        item.offsetIncrement = 0
        item.visible = false
        visibility[index] = item.visible
    })
    settingTitle.value = ''
    startOffset()
}

// 获取曲线数据
const getDeviceData = async () => {
    dataStatus.value = 'loading'
    // 根据选择的设备id查找对应的设备名称，显示在图表标题上
    const selectedDevice = deviceList.value.find(
        (device) => device.id === deviceId.value
    )
    if (selectedDevice) {
        // 更新标题，将选中的设备名称添加到标题中
        title.value = `${dateData.value} ${selectedDevice.name}`
    } else {
        // 如果没有找到选中的设备，使用默认标题
        title.value = `${dateData.value} 未知设备`
    }
    await getDeviceCsv(deviceId.value, dateData.value)
        .then((res) => {
            // console.log(res) // 打印文件内容
            // if (res === '无内容') {
            //     ElMessage({
            //         message: h(
            //             'p',
            //             { style: 'line-height: 1; font-size: 14px' },
            //             [
            //                 h(
            //                     'span',
            //                     null,
            //                     '当前设备所选日期文件为空，请重新选择'
            //                 ),
            //             ]
            //         ),
            //     })
            // }
            csvData.value = res
        })
        .catch((error) => {
            console.error('获取 CSV 文件内容时出错：', error)
        })
    if (csvData.value === '无内容') {
        dataStatus.value = 'error'
        return
    }
    dataStatus.value = 'ok'
    if (graph) {
        // 清空当前图形
        graph.destroy()
        graph = null
    }
    // 重新初始化图形
    await getCsvHeadersName(csvData.value)
    const graphDiv = document.getElementById('graphDiv')
    initializeGraph(graphDiv)
}

// 页面主题控制函数
function styleChange() {
    const contentStyle = document.getElementById('appContent')
    if (contentStyle) {
        if (subjectStyle.value === false) {
            contentStyle.style.color = '#ffffff'
            contentStyle.style.backgroundColor = '#000000'
        } else {
            contentStyle.style.color = '#000000'
            contentStyle.style.backgroundColor = '#ffffff'
        }
    }
    window.localStorage.setItem('subjectStyle', subjectStyle.value)
}

const getRowStyle = ({ rowIndex }) => {
    // 根据颜色数组的索引动态设置背景颜色
    return {
        color: colors[rowIndex % colors.length],
        fontWeight: 'bold',
    }
}

// 控制抽屉函数
function showDrawer() {
    drawer.value = !drawer.value
}

// 添加设置函数
async function addSettings() {
    const body = {
        title: settingName.value,
        deviceId: deviceId.value,
        fileTime: dateData.value + ' 00:00:00',
        confTag: curveShow.value,
    }
    console.log(body)
    await addDeviceSettings(body)
}

// 获取设置函数
async function getSetting() {
    const body = {
        pageNum: 1,
        pageSize: 10000,
        orderByColumn: 'update_time',
        orderDirect: 'desc',
        startTime: dateData.value,
        endTime: dateData.value,
        deviceId: deviceId.value,
    }
    const res = await getDeviceSettings(body)
    console.log(res)
    if (res.code === 200) {
        settingOptions.value = res.rows.map((item) => ({
            title: item.title,
            id: item.id,
        }))
    }
}

// 根据设置的id来获取设置
async function idGetSettings() {
    const body = {
        id: settingTitle.value,
    }
    console.log(title, body)
    const res = await getSettingsInfoUseId(body)
    curveShow.value = res.data.confTag
    curveShow.value.forEach((item, index) => {
        visibility[index] = item.visible
    })
    startOffset()
}

// 确认按钮，用来重绘偏移的图
function startOffset() {
    const graphDiv = document.getElementById('graphDiv')
    if (graph) {
        // 保存当前的缩放范围
        const currentZoom = graph.xAxisRange()
        // 清空当前图形
        graph.destroy()
        graph = null
        initializeGraph(graphDiv)
        // 恢复之前的缩放范围
        if (currentZoom) {
            graph.updateOptions({
                dateWindow: currentZoom,
                visibility,
                // valueRange: [0, max + 1000]
            })
        }
    }
}

// 获取csv表的表头数据
async function getCsvHeadersName(csvContent) {
    try {
        // 读取文件内容为文本
        const text = csvContent
        console.log('text:', text, typeof text)
        const lines = text.split('\n') // 按行分割
        // 第一行是表头，按逗号分割
        const headers = lines[0].split(',')
        csvHeaders.value = headers.map((item, index) => ({
            value: item,
            label: item,
            index,
        }))
        csvHeaders.value.shift() // 删除第一行（如果第一行不是表头）
        curveShow.value = []
        // 每次获取到csv文件后，进行曲线数组赋值，曲线数组包括曲线名（列名）、曲线
        curveShow.value = csvHeaders.value.map((item) => ({
            name: item.value,
            index: item.index,
            offsetMultiplication: 1,
            offsetIncrement: 0,
            visible: false,
        }))
        visibility = new Array(csvHeaders.value.length).fill(false)
        console.log(visibility)
    } catch (error) {
        console.error('Error parsing CSV headers:', error)
        csvHeaders.value = [] // 如果出错，清空表头数组
    }
}

// 自定义绘图函数
function customPlotter(e) {
    const g = e.dygraph
    const series = e.seriesIndex
    const ctx = e.drawingContext
    const points = e.allSeriesPoints[series] // 获取全部系列的数据点
    // 因为曲线绘制时，它内置循环会检测visibility属性，如果有false则直接不会进行循环，循环次数会少false的个数次，因此直接进行赋值会导致错位，使得曲线数据变化
    // 所以要检测当前是否为false，如果是false就跳过，直到找到true即需要绘制的曲线，这时候再将该索引下的数值赋值给曲线绘制的yval,这样就能解决错位的问题了
    for (let i = count; i < curveShow.value.length; i++) {
        if (curveShow.value[i].visible === true) {
            count = i
            break
        }
    }
    // 根据偏移修改y值
    if (curveShow.value) {
        // console.log('曲线绘制的第：', series, '数组的第：', count) // 检测是否错位
        // 防止用户在偏移的乘积上填入0或负数，如果填了就默认为乘数为0
        if (curveShow.value[count].offsetMultiplication <= 0) {
            curveShow.value[count].offsetMultiplication = 1
        }
        // 如果用户没有填写则默认加数为0
        if (!curveShow.value[count].offsetIncrement) {
            curveShow.value[count].offsetIncrement = 0
        }
        for (let i = 0; i < points.length; i++) {
            points[i].yval =
                points[i].yval *
                    Number(curveShow.value[count].offsetMultiplication) +
                Number(curveShow.value[count].offsetIncrement)
            // max = max > points[i].yval ? max : points[i].yval
            points[i].canvasy = g.toDomCoords(points[i].xval, points[i].yval)[1]
        }
    }

    ctx.beginPath()
    ctx.strokeStyle = e.color
    ctx.lineWidth = 3 // 控制线条粗细

    for (let i = 0; i < points.length - 1; i++) {
        if (i === 0) {
            ctx.moveTo(points[0].canvasx, points[0].canvasy)
            continue
        }
        const p1 = points[i]
        const p2 = points[i - 1]

        if (p1.xval - p2.xval > 1000) {
            // 如果x值的差大于 1000，则跳过绘制，检测是文件中是否有时间数据的缺失
            // 因为曲线的横坐标在csv中是以ISO 8601格式存储的，但Dygraphs会将其转化为纯数字的时间戳，一秒的差值就是1000
            ctx.moveTo(p1.canvasx, p1.canvasy)
            continue
        }

        if (isNaN(p2.yval)) {
            ctx.moveTo(p1.canvasx, p1.canvasy)
            continue
        }

        ctx.lineTo(p1.canvasx, p1.canvasy)
    }

    ctx.stroke()
    count++
    // 判断如果绘制到了最后一条线，则将计数器清0
    if (series === e.seriesCount - 1) {
        count = 0
    }
}

// 接收参数yval（y值）, opts（函数）, seriesName（列名）, g（Dygraphs）, row（行号）, col（列号）
function showLabel(yval, opts, seriesName, g, row, col) {
    if (col === 0) {
        const date = new Date(yval)
        const year = date.getFullYear()
        const month = String(date.getMonth() + 1).padStart(2, '0')
        const day = String(date.getDate()).padStart(2, '0')
        const hours = String(date.getHours()).padStart(2, '0')
        const minute = String(date.getMinutes()).padStart(2, '0')
        const second = String(date.getSeconds()).padStart(2, '0')
        return `${year}-${month}-${day} ${hours}:${minute}:${second}`
    }
    // 获取输入值
    const Jia = Number(curveShow.value[col - 1].offsetIncrement)
    const Che = Number(curveShow.value[col - 1].offsetMultiplication)

    // 根据公式计算并格式化显示的内容
    const calculatedValue = (yval - Jia) / Che
    return `${calculatedValue} * ${Che} + ${Jia}` // 格式化为两位小数
}

function initializeGraph(graphDiv) {
    graph = new Dygraph(graphDiv, csvData.value, {
        title: title.value,
        titleHeight: 50, // 图标的表头高度
        xlabel: '时间',
        ylabel: '数值',
        axisLabelWidth: 50, //轴标签宽度
        drawPoints: true, // 是否绘制曲线上的数值点
        colors, // 颜色数组，从自定义的色卡中选，按顺序为每条线设置，共有50种颜色，如果不足五十条线，则颜色时有多少条线选到多少颜色
        labelsDiv: document.getElementById('content'),
        legend: 'follow',
        strokeBorderWidth: 2, // 线条边框宽度
        strokeBorderColor: ['blue', 'pink'], // 线条边框颜色
        strokeWidth: 3.0, // 曲线粗细 (这里因为定义了自定义的绘制，也可以在customPlotter中进行曲线粗细设置，并且优先级更高，会是此处不起作用)
        // axisLineColor: 'gold', // 坐标轴颜色
        labelsUTC: true, // 标签是否使用UTC时间，如果选择false会成为UTC+8
        gridLineWidth: 2, // 网格线宽度
        // displayAnnotations: true,
        hideOverlayOnMouseOut: false, // 鼠标移出图标区时是否隐藏图例
        // fillGraph：填充折线下方（类似阴影颜色）如果需要增加阴影，会导致渲染速度的减缓，如果数据量很大，会导致数据渲染异常缓慢（大概有三、四分钟），甚至无法渲染至浏览器崩溃
        // fillGraph: true,
        highlightCircleSize: 5, // 曲线中数值点的大小
        labelsSeparateLines: true, // 图例标签是否每条换行显示,
        visibility,
        // valueRange: [0, 15000], // 纵轴的范围
        // 界面缩放到最小时只显示十秒的内容的回调函数（但目前会在缩放至最小时再次缩放会进行重绘，导致位置偏移，但仍是十秒），缩放回调函数
        zoomCallback: function (minDate, maxDate) {
            // 限制最小缩放范围为10秒
            const minZoomRange = 10 * 1000 // 10秒转换为毫秒
            if (maxDate - minDate < minZoomRange) {
                graph.updateOptions({
                    dateWindow: [minDate, minDate + minZoomRange],
                    visibility,
                })
            }
        },
        axes: {
            // 横坐标显示正常日期的回调函数
            // 因为现在csv文件中的时间列使用的是ISO 8601的格式时间，使用的是UTC时间，如果不作处理展示需要所有时间-8h
            // 后续可以修改为UTC+8的时间
            x: {
                pixelsPerLabel: 150, // 控制x轴格子宽度
                axisLabelFormatter: function (d, granularity, opts, dygraph) {
                    const date = new Date(d)
                    const hours = String(date.getHours()).padStart(2, '0')
                    const minute = String(date.getMinutes()).padStart(2, '0')
                    const second = String(date.getSeconds()).padStart(2, '0')
                    return `${hours}:${minute}:${second}`
                },
            },
            y: {
                pixelsPerLabel: 30, //控制y轴的格子高度
            },
        },
        // 数据绘制处理回调函数,这里通过自定义的方式，进行曲线绘制，解决csv中的信息缺失，则在相应位置不进行绘制的问题，并且会增加渲染速度
        plotter: [customPlotter], // 使用自定义绘图函数
        valueFormatter: showLabel, // 使用自定义值格式化函数
    })
}

// 监听csv文件是否发生改变
// watch(csvData, async (newcsvData) => {
//     if (graph) {
//         // 清空当前图形
//         graph.destroy()
//         graph = null
//     }
//     // 重新初始化图形
//     await getCsvHeadersName(csvData)
//     const graphDiv = document.getElementById('graphDiv')
//     initializeGraph(graphDiv)
// })

function curveVisible(index) {
    if (graph) {
        // 需要使用true和false,如果使用1和0择不生效
        visibility[index - 1] = curveShow.value[index - 1].visible
        graph.setVisibility(visibility)
    }
}

onMounted(async () => {
    console.log(window.localStorage.getItem('subjectStyle'))
    subjectStyle.value = window.localStorage.getItem('subjectStyle')
        ? Boolean(JSON.parse(window.localStorage.getItem('subjectStyle')))
        : false
    styleChange()
    const body = {
        groupId: '13dea300f0802f48',
        delFlag: '0',
    }
    deviceList.value = await getDeviceList(body)
    console.log('获取的列表', deviceList.value)
    curveShow.value = [] // 初始化曲线数据
    visibility = [] // 初始化可见性数组
    // 使用await来等待csv文件为curveShow数组赋值，否则会在赋值过程中进行绘制，导致curveShow报错未有属性
    // await getDeviceData()
})

onUnmounted(() => {
    if (graph) {
        graph.destroy()
        graph = null
    }
})
</script>

<style scoped>
@import '../assets/element-plus.css';
/* 主体 */
.content {
    width: 100%;
    height: 100%;
    background-color: #ffffff;
    color: #000000;
    position: relative;
}

/* 抽屉 */
.drawer {
    position: absolute;
    width: 30px;
    height: 2%;
    top: 20%;
    left: 0;
    cursor: pointer;
    z-index: 1;
}

.tip {
    position: absolute;
    width: 50%;
    height: 50%;
    left: 50%;
    top: 50%;
    margin-top: -20%;
    margin-left: -20%;
}

.tip--text {
    font-size: 50px;
    text-align: center;
}

.iconRight {
    position: absolute;
    width: 30px;
    height: 2%;
    top: 20%;
    right: 0;
    cursor: pointer;
    color: #909399;
}

/* 数据曲线盒子 */
.chart {
    width: 80%;
    height: 100%;
    position: absolute;
    top: 0;
    right: 2%;
}

/* Dygraphs盒子 */
.graphDiv {
    position: absolute;
    margin-top: -550px;
    top: 50%;
    right: 0;
}

/* 图例标签盒子 */
.labelDiv {
    position: absolute;
    width: 12%;
    height: 96%;
    top: 2%;
    left: 2%;
    overflow: scroll;
    border: 1px solid gray;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

.subject {
    display: flex;
    justify-content: space-around;
    align-items: center;
}

:deep(.drawerMask) {
    width: 18%;
}

.drawerContent {
    width: 100%;
    background-color: #21262f;
}

.drawerContent--setting {
    position: absolute;
    width: calc(100% - 50px);
    height: 100%;
    left: 0;
}

/* 日期选择器 */
.selectDate {
    display: flex;
    align-items: center;
    justify-content: space-around;
    margin-top: 10%;
}

.deviceId {
    display: flex;
    align-items: center;
    justify-content: space-around;
    margin-top: 5%;
}

/* 设置下拉框 */
.selectSetting {
    display: flex;
    align-items: center;
    justify-content: space-around;
    margin-top: 5%;
}

/* 数据表格 */
.dataTable {
    position: absolute;
    top: 25%;
    overflow: scroll;
}

.clickButton {
    width: 80%;
    height: 60px;
    position: absolute;
    top: 20%;
    left: 10%;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.button {
    width: 80px;
}

:deep(.dygraph-xlabel) {
    text-align: right;
}
:deep(.dygraph-ylabel) {
    width: 80px;
    margin-top: -52%;
    margin-left: -1%;
}
:deep(.dygraph-title) {
    text-align: center;
    margin-top: -30px;
}
:deep(.dygraph-label-rotate-right) {
    left: 0 !important;
}

/* 定义滚动条整体部分 */
::-webkit-scrollbar {
    display: none;
}

/* 定义滚动条轨道 */
::-webkit-scrollbar-track {
    -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); /* 内阴影 */
    border-radius: 10px; /* 圆角 */
    background-color: #f5f5f5; /* 轨道背景色 */
}

/* 定义滚动条滑块 */
::-webkit-scrollbar-thumb {
    border-radius: 10px; /* 圆角 */
    -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); /* 内阴影 */
    background-color: #555; /* 滑块背景色 */
}
</style>
